#include "common.hpp"
#include <iostream>
using namespace sv;


Var::Var(double coef, VarType type_) :
	col(0), 
	val(0), 
	coeffs(coef), 
	type(type_) 
{
};

double Var::get(DoubleAttr attr)
{
	switch (attr) {
	case DoubleAttr::Coeff:
		return coeffs;
	case DoubleAttr::X:
		return val;
	}
	return -1;
}

int Var::get(IntAttr attr)
{
	return col;
}

Expr sv::operator+(const Expr& x, const Expr& y)
{
	Expr exp;
	exp.coeffs.resize(std::max(x.coeffs.size(), y.coeffs.size()), 0);
	for (int c = 0; c < exp.coeffs.size(); c++) {
		if (c < x.coeffs.size() && c < y.coeffs.size()) {
			exp.coeffs.at(c) = x.coeffs.at(c) + y.coeffs.at(c);
		}
		else {
			exp.coeffs.at(c) = c < x.coeffs.size() ? x.coeffs.at(c) : y.coeffs.at(c);
		}
	}
	exp.constant = x.constant + y.constant;
	return exp;
}

Expr sv::operator+(const Expr& x)
{
	return x;
}

Expr sv::operator+(Var x, Var y)
{
	Expr exp;
	exp.coeffs.resize(std::max(x.get(IntAttr::NumCol) + 1, y.get(IntAttr::NumCol) + 1), 0);
	exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff);
	exp.coeffs.at(y.get(IntAttr::NumCol)) = y.get(DoubleAttr::Coeff);
	return exp;
}

Expr sv::operator+(Var x, double a)
{
	Expr exp;
	exp.coeffs.resize(x.get(IntAttr::NumCol) + 1);
	exp.constant = a;
	return exp;
}

Expr sv::operator+(double a, Var x)
{
	return x + a;
}

Expr sv::operator-(const Expr& x, const Expr& y)
{
	Expr exp;
	exp.coeffs.resize(std::max(x.coeffs.size(), y.coeffs.size()), 0);
	for (int c = 0; c < exp.coeffs.size(); c++) {
		if (c < x.coeffs.size() && c < y.coeffs.size()) {
			exp.coeffs.at(c) = x.coeffs.at(c) - y.coeffs.at(c);
		}
		else {
			exp.coeffs.at(c) = c < x.coeffs.size() ? x.coeffs.at(c) : y.coeffs.at(c);
		}
	}
	exp.constant = x.constant + y.constant;
	return exp;
}

Expr sv::operator-(const Expr& x)
{
	Expr expr(x);
	for (int c = 0; c < expr.coeffs.size(); c++) {
		expr.coeffs.at(c) = -expr.coeffs.at(c);
	}
	expr.constant = -expr.constant;
	return expr;
}

Expr sv::operator-(Var x)
{
	return -Expr(x);
}

Expr sv::operator-(Var x, Var y)
{
	Expr exp;
	exp.coeffs.resize(std::max(x.get(IntAttr::NumCol) + 1, y.get(IntAttr::NumCol) + 1), 0);
	exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff);
	exp.coeffs.at(y.get(IntAttr::NumCol)) = -y.get(DoubleAttr::Coeff);
	return exp;
}

Expr sv::operator-(Var x, double a)
{
	return x - Var(a);
}

Expr sv::operator-(double a, Var x)
{
	return x - a;
}

Expr sv::operator*(double a, Var x)
{
	Expr exp;
	exp.coeffs.resize(x.get(IntAttr::NumCol) + 1, 0);
	exp.coeffs.at(x.get(IntAttr::NumCol)) = a * x.get(DoubleAttr::Coeff);
	return exp;
}

Expr sv::operator*(Var x, double a)
{
	return a * x;
}

Expr sv::operator*(const Expr& x, double a)
{
	Expr exp = x;
	for (int c = 0; c < exp.coeffs.size(); c++) {
		exp.coeffs.at(c) *= a;
	}
	exp.constant *= a;
	return exp;
}

Expr sv::operator*(double a, const Expr& x)
{
	return x * a;
}

Expr sv::operator/(Var x, double a)
{
	return Expr(x) / a;
}

Expr sv::operator/(const Expr& x, double a)
{
	Expr exp = x;
	for (int c = 0; c < exp.coeffs.size(); c++) {
		exp.coeffs.at(c) /= a;
	}
	exp.constant /= a;
	return exp;
}

Expr::Expr(double constant)
	:constant(constant)
{
}

Expr::Expr(Var var, double coeff)
{
	this->coeffs.resize(var.col + 1);
	this->coeffs.at(var.col) = coeff;
	this->constant = 0;
}

Expr Expr::operator=(const Expr& rhs)
{
	return *this;
}

void Expr::operator+=(const Expr& expr)
{
	coeffs.resize(std::max(coeffs.size(), expr.coeffs.size()));
	for (int c = 0; c < expr.coeffs.size(); c++) {
		coeffs.at(c) += expr.coeffs.at(c);
	}
	constant += expr.constant;
}

void Expr::operator-=(const Expr& expr)
{
	coeffs.resize(std::max(coeffs.size(), expr.coeffs.size()));
	for (int c = 0; c < expr.coeffs.size(); c++) {
		coeffs.at(c) -= expr.coeffs.at(c);
	}
	constant -= expr.constant;
}

void Expr::operator*=(double mult)
{
	for (int c = 0; c < coeffs.size(); c++) {
		coeffs.at(c) *= mult;
	}
	constant *= mult;
}

void Expr::operator/=(double a)
{
	for (int c = 0; c < coeffs.size(); c++) {
		coeffs.at(c) /= a;
	}
	constant /= a;
}

Expr Expr::operator+(const Expr& rhs)
{
	coeffs.resize(std::max(coeffs.size(), rhs.coeffs.size()));
	for (int c = 0; c < rhs.coeffs.size(); c++) {
		coeffs.at(c) += rhs.coeffs.at(c);
	}
	constant += rhs.constant;
	return *this;
}

Expr Expr::operator-(const Expr& rhs)
{
	coeffs.resize(std::max(coeffs.size(), rhs.coeffs.size()));
	for (int c = 0; c < rhs.coeffs.size(); c++) {
		coeffs.at(c) -= rhs.coeffs.at(c);
	}
	constant -= rhs.constant;
	return *this;
}
